home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Other / GACUPS / Source / UPSController.m < prev   
Encoding:
Text File  |  1993-08-13  |  7.1 KB  |  253 lines

  1. /* Written 8/12/93 by Max Hailperin <max@nic.gac.edu>, Math/CS department,
  2.    Gustavus Adolphus College.  Public domain, no warranty.  Please share
  3.    any improvements with me. 
  4.  
  5.    Note: this communications protocol is for use with Best Fortress UPSs. */
  6.  
  7. #import "daemon.h"
  8. #import "UPSController.h"
  9. #import <sys/ioctl.h>
  10. #import <sys/fcntl.h>
  11. #import <dpsclient/dpsclient.h>
  12. #import <appkit/Application.h>
  13. #import <stdio.h>
  14. #import <stdlib.h>
  15. #import <libc.h>
  16. #import <time.h>
  17. #import <ctype.h>
  18. #import <syslog.h>
  19.  
  20. #ifndef POLLING_PERIOD
  21. #define POLLING_PERIOD 60.0
  22. #endif
  23. #ifndef RETRY_PERIOD
  24. #define RETRY_PERIOD 5.0
  25. #endif
  26. #ifndef RETRIES
  27. #define RETRIES 3
  28. #endif
  29. #ifndef SHUTDOWN_MINUTES
  30. #define SHUTDOWN_MINUTES 5
  31. #endif
  32. #ifndef SHUTDOWN_ALARM_MASK
  33. #define SHUTDOWN_ALARM_MASK 0x292
  34. #endif
  35. #ifndef SHUTDOWN_DELAY
  36. #define SHUTDOWN_DELAY 60
  37. #endif
  38. #ifndef POWERDOWN_DELAY
  39. #define POWERDOWN_DELAY 120
  40. #endif
  41.  
  42. #define BYTES_IN_F_STRING 40
  43. #define HEX_DIGITS_PER_BYTE 2
  44.  
  45. #define ctrl(c) ((c)&0x1f)
  46.  
  47. static struct {
  48.   int systemMode, inverterStatus, alarmStatus, ACInputVolts, ACOutputVolts,
  49.   ACOutputDeciAmperes, ACLoadVA, batteryDeciVolts, deciHertz, minutes,
  50.   ambientTemp, ROMVersion, time;
  51. } status;
  52.  
  53. static DPSTimedEntry retryTE;
  54. static int retries;
  55.  
  56. static int checksumOK(const unsigned char *fString){
  57.   unsigned char sum=0;
  58.   int i;
  59.   for(i = 0; i < BYTES_IN_F_STRING; i++)
  60.     sum += fString[i];
  61.   return sum == 0;
  62. }
  63.  
  64. static int asDecimal(const unsigned char *bytes, int count){
  65.   int value=0;
  66.   while(count--){
  67.     value *= 100;
  68.     value += 10*(*bytes/16) + *bytes % 16;
  69.     bytes++;
  70.   }
  71.   return value;
  72. }
  73.  
  74. static void describeTrouble(){
  75.   if(status.inverterStatus)
  76.     syslog(FACILITY|LOG_EMERG,
  77.            "Running on battery power; backup time remaining: %d minutes.",
  78.            status.minutes);
  79.   if(status.alarmStatus & 0x1)
  80.     syslog(FACILITY|LOG_EMERG, "UPS memory error");
  81.   if(status.alarmStatus & 0x2)
  82.     syslog(FACILITY|LOG_EMERG, "Check battery");
  83.   if(status.alarmStatus & 0x4)
  84.     syslog(FACILITY|LOG_EMERG, "Check inverter");
  85.   if(status.alarmStatus & 0x8)
  86.     syslog(FACILITY|LOG_EMERG, "Low runtime remaining");
  87.   if(status.alarmStatus & 0x10)
  88.     syslog(FACILITY|LOG_EMERG, "Low battery");
  89.   if(status.alarmStatus & 0x20)
  90.     syslog(FACILITY|LOG_EMERG, "Circuit breaker shutdown");
  91.   if(status.alarmStatus & 0x40)
  92.     syslog(FACILITY|LOG_EMERG, "Overload");
  93.   if(status.alarmStatus & 0x80)
  94.     syslog(FACILITY|LOG_EMERG, "High temperature");
  95.   if(status.alarmStatus & 0x100)
  96.     syslog(FACILITY|LOG_EMERG, "Site wiring fault");
  97.   if(status.alarmStatus & 0x200)
  98.     syslog(FACILITY|LOG_EMERG, "High battery");
  99. }
  100.  
  101. static void doShutdown(FILE *ups){
  102.   syslog(FACILITY|LOG_EMERG,
  103.          "Warning!!!  System shutdown in %d seconds; please log off NOW!",
  104.          SHUTDOWN_DELAY);
  105. #ifndef DEBUG
  106.   sleep(SHUTDOWN_DELAY);
  107.   fprintf(ups, "off %d\r", POWERDOWN_DELAY);
  108.   system("/usr/etc/halt -p");
  109. #endif
  110. }
  111.  
  112. static void handleTrouble(FILE *ups){
  113.   /* should do the filtering of transients like checkups somehow? */
  114.   describeTrouble();
  115.   if((status.alarmStatus & SHUTDOWN_ALARM_MASK) ||
  116.      (status.inverterStatus && status.minutes < SHUTDOWN_MINUTES))
  117.     doShutdown(ups);
  118. }
  119.  
  120. static void processFString(const unsigned char *fString, FILE *ups){
  121.   if(!checksumOK(fString))
  122.     syslog(FACILITY|LOG_CRIT, "Checksum error in UPS F string");
  123.   else{
  124.     if(retryTE){
  125.       DPSRemoveTimedEntry(retryTE);
  126.       retryTE = 0;
  127.     }
  128.     status.systemMode = fString[5];
  129.     status.inverterStatus = fString[8];
  130.     status.alarmStatus = fString[11]<<8 + fString[10];
  131.     status.ACInputVolts = asDecimal(fString+12, 2);
  132.     status.ACOutputVolts = asDecimal(fString+14, 2);
  133.     status.ACOutputDeciAmperes = asDecimal(fString+18, 2);
  134.     status.ACLoadVA = asDecimal(fString+20, 3);
  135.     status.batteryDeciVolts = asDecimal(fString+25, 2);
  136.     status.deciHertz = asDecimal(fString+27, 2)/10;
  137.     status.minutes = asDecimal(fString+29, 2);
  138.     status.ambientTemp = asDecimal(fString+31, 2);
  139.     status.ROMVersion = asDecimal(fString+37, 2);
  140.     status.time = time(0);
  141.     if(status.inverterStatus || status.alarmStatus)
  142.       handleTrouble(ups);
  143.   }
  144. }
  145.  
  146. static void reallyRequestFString(FILE *ups){
  147.   putc('f', ups);
  148.   putc('\r', ups);
  149. }
  150.  
  151. static void retry(DPSTimedEntry retryTE, double now, void *ups){
  152. #ifdef DEBUG
  153. printf(">>>retrying\n");
  154. #endif
  155.   if(retries++ == RETRIES)
  156.     syslog(FACILITY|LOG_CRIT, "Not succesfully communicating with UPS");
  157.   putc(ctrl('q'), (FILE*)ups);
  158.   reallyRequestFString((FILE*)ups);
  159. }
  160.  
  161. static void requestFString(FILE *ups){
  162.   if(!retryTE){
  163.     retries = 0;
  164.     retryTE= DPSAddTimedEntry(RETRY_PERIOD, retry, ups, NX_RUNMODALTHRESHOLD);
  165.     reallyRequestFString(ups);
  166.   }
  167. }
  168.   
  169. static void handleInput(int fd, void* vfp){
  170.   static unsigned char bytes[BYTES_IN_F_STRING];
  171.   static char hexDigits[HEX_DIGITS_PER_BYTE+1];
  172.   static unsigned char *bytePtr=bytes;
  173.   static char *hexPtr=hexDigits;
  174.   int c;
  175.   FILE *fp = (FILE*)vfp;
  176.  
  177.   while((c = getc(fp)) != EOF){
  178. #ifdef DEBUG
  179. putchar(c);
  180. #endif
  181.     if(c == '\r'
  182.        && bytePtr-bytes == BYTES_IN_F_STRING
  183.        && hexPtr == hexDigits){
  184.       processFString(bytes, fp);
  185.       bytePtr = bytes;
  186.     } else if(!isxdigit(c)){
  187.       if(c == '[' || c == ']' || c == '{' || c == '}')
  188.         requestFString(fp);
  189.       bytePtr = bytes;
  190.       hexPtr = hexDigits;
  191.     } else{
  192.       *hexPtr++ = c;
  193.       if(hexPtr-hexDigits == HEX_DIGITS_PER_BYTE){
  194.         if(bytePtr-bytes == BYTES_IN_F_STRING){
  195.           syslog(FACILITY|LOG_CRIT, "Too many hex digits in a row from UPS");
  196.           bytePtr = bytes;
  197.         }
  198.         *bytePtr++ = strtol(hexDigits, NULL, 16);
  199.         hexPtr = hexDigits;
  200.       }
  201.     }
  202.   }
  203.   clearerr(fp);
  204. }
  205.  
  206. static void poll(DPSTimedEntry pollTE, double now, void *ups){
  207.   requestFString((FILE*)ups);
  208. }
  209.  
  210. @implementation UPSController
  211.  
  212. -initOn: (const char *)tty
  213. {
  214.   struct sgttyb modes = {B1200, B1200, 0, 0, RAW};
  215.   FILE *ups;
  216.   if((ups = fopen(tty, "r+")) == NULL){
  217.         syslog(FACILITY|LOG_CRIT, "Can't open alleged tty %s; exiting.", tty);
  218.         exit(1);
  219.   }
  220.   setbuf(ups, NULL);
  221.   ioctl(fileno(ups), TIOCSETP, &modes);
  222.   fcntl(fileno(ups), F_SETFL, FNDELAY);
  223.   DPSAddFD(fileno(ups), handleInput, ups, NX_MODALRESPTHRESHOLD);
  224.   requestFString(ups);
  225.   DPSAddTimedEntry(POLLING_PERIOD, poll, ups, NX_RUNMODALTHRESHOLD);
  226.   return self;
  227. }
  228.  
  229. -(int)systemMode:(int*)systemMode inverterStatus:(int*)inverterStatus
  230.       alarmStatus:(int*)alarmStatus ACInputVolts:(int*)ACInputVolts
  231.       ACOutputVolts:(int*)ACOutputVolts
  232.       ACOutputDeciAmperes:(int*)ACOutputDeciAmperes ACLoadVA:(int*)ACLoadVA
  233.       batteryDeciVolts:(int*)batteryDeciVolts deciHertz:(int*)deciHertz
  234.       minutes:(int*)minutes ambientTemp:(int*)ambientTemp
  235.       ROMVersion:(int*)ROMVersion time:(int*)time
  236. {
  237.   *systemMode = status.systemMode;
  238.   *inverterStatus = status.inverterStatus;
  239.   *alarmStatus = status.alarmStatus;
  240.   *ACInputVolts = status.ACInputVolts;
  241.   *ACOutputVolts = status.ACOutputVolts;
  242.   *ACOutputDeciAmperes = status.ACOutputDeciAmperes;
  243.   *ACLoadVA = status.ACLoadVA;
  244.   *batteryDeciVolts = status.batteryDeciVolts;
  245.   *deciHertz = status.deciHertz;
  246.   *minutes = status.minutes;
  247.   *ambientTemp = status.ambientTemp;
  248.   *ROMVersion = status.ROMVersion;
  249.   *time = status.time;
  250.   return 0;
  251. }
  252. @end
  253.